

#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>			// O_RDWR
#include <string.h>			// strerror(), memset()
#include <netdb.h>
#include <unistd.h>			// dup(), fork()
#include <termios.h>

#include <map>
#include <iostream>
#include <cstdlib>  		//	EXIT_SUCCESS, atoi(), getenv(), setenv()
#include <tuple>

#include "descriptor.h"

//------------------------------------------------------------------------------------------------------------------
using std::string;
typedef std::map<std::string, speed_t> baud_rate_map_t;
typedef std::tuple<bool, bool> rw_t;
//------------------------------------------------------------------------------------------------------------------
void exit_with_error(const char * msg)
{
	string m(msg);
	m += string(" - errno [") + string(strerror(errno)) + string("]");
	setenv("seweb_CLIENT_ERROR", m.c_str(), 1);
	std::cout << "seweb_CLIENT_ERROR - " << m << std::endl;
	exit(EXIT_FAILURE);
}
//------------------------------------------------------------------------------------------------------------------
// Technical notes in: http://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux
//                     http://linux.die.net/man/3/termios
//                     http://rachid.koucha.free.fr/tech_corner/pty_pdip.html
//                     http://www.easysw.com/~mike/serial/serial.html
struct tty_t
{
	bool _ready;
	int _fd;
	std::string _path;	
	speed_t _speed;
	baud_rate_map_t _baud_rate;
	struct termios _tio;	
	
	tty_t(const char * path, const char * speed) 
		: _ready(false)
		, _fd(-1)
		, _path(path)
		, _speed(B115200)
	{
		/*
		_baud_rate ={ {"B0", B0}, {"B50", B50}, {"B75", B75}, {"B110", B110}, {"B134", B134}
			        , {"B150", B150}, {"B200", B200}, {"B300", B300}, {"B600", B600}
			        , {"B1200", B1200}, {"B1800", B1800}, {"B2400", B2400}, {"B4800", B4800}
			        , {"B9600", B9600}, {"B19200", B19200}, {"B38400", B38400}, {"B57600", B57600}
			        , {"B115200", B115200}, {"B230400", B230400}};
	
		_tio.c_iflag     = 0;							// imput modes
		_tio.c_oflag     = 0;							// output modes
		_tio.c_cflag     = CS8|CREAD|CLOCAL;           	// control modes. 8n1, see termios.h for more information
														//		CS8 - character size mask.
														// 		CREAD - enable receiver.
														//		CLOCAL - ignore modem control lines.
		_tio.c_lflag     = 0;							// local modes.
		_tio.c_cc[VMIN]  = 1;							// special characters. Minimum number of characters for noncanonical read (MIN).
		_tio.c_cc[VTIME] = 5;							// Timeout in deciseconds for noncanonical read (TIME).
		
		if (_baud_rate.count(speed))
			_speed = _baud_rate[speed];
		
	 	cfgetospeed(&_tio);
	 	
		cfsetospeed(&_tio, _speed);
		cfsetispeed(&_tio, _speed);
		tcsetattr(_fd, TCSANOW, &_tio);		// TCSANOW: the change occurs immediately.		
		*/
			
	 	if ((_fd = open(_path.c_str(), O_RDWR | O_NONBLOCK)) != -1) {	 			 					
			memset(&_tio, 0, sizeof(_tio));	
			if (tcgetattr(_fd, &_tio) == -1)
				exit_with_error("tcgetattr()");
			
			_ready = true;
	 	}
	}
	
	~tty_t() 
	{
		close(_fd);
	}
};
//------------------------------------------------------------------------------------------------------------------
void process_server_requests(int serv_fd, tty_t & tty)
{
	using namespace tty::common::sys;
	
	for (;;) {
		if (get_descriptor_status(serv_fd, 0, 50000000 /* 0.05 sec */, e_rw::read_fd)) {			
			
		}		
	}
}
//------------------------------------------------------------------------------------------------------------------
void dispatch_serial_output_to_server(int serv_fd, tty_t & tty)
{		
	using namespace tty::common::sys;
	
	const int BUFSIZE = 1024;
	char buf[BUFSIZE];
	int nread = 0;
	int sent = 0;	
	
	for (;;) {		
		if (get_descriptor_status( tty._fd 
				                 , 0
				                 , 50000000 /* 0.05 sec */
				                 , e_rw::read_fd
				                 , 1000 /* 50000000 * 1000 = 50 sec */ )) {
		
			memset(buf, 0, sizeof(buf));
			nread = read(tty._fd, buf, BUFSIZE);
			if (-1 == nread && errno == EINTR) break;  	// Signal detected.
			
			if (0 == nread) continue;	// EOF
				
			if (get_descriptor_status(serv_fd, 0, 50000000 /* 0.05 sec */, e_rw::write_fd)) {			
				if ((sent = send(serv_fd, buf, nread, MSG_DONTWAIT)) < 0) {
					close(serv_fd);
					exit_with_error("send()");
				}
				else if (sent != nread) {	/* Something wrong happend. */	}
			}
		}
	}
}
//------------------------------------------------------------------------------------------------------------------
int connect_to_seweb_server(const char * host)
{
	char service[] = "12002";		// char *  --> is deprecated
		
	struct addrinfo hints;
	struct addrinfo *result; 
		
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_canonname = NULL;
	hints.ai_addr      = NULL;
	hints.ai_next      = NULL;
	hints.ai_family    = AF_UNSPEC;			// Allows IPv4 or IPv6
	hints.ai_socktype  = SOCK_STREAM;
	hints.ai_flags     = AI_NUMERICSERV;	// The service must point to a string containing a 
											// numeric port number.
	
	if (getaddrinfo(host, service, &hints, &result) != 0)
		exit_with_error("getaddrinfo()");	
	
	struct addrinfo * rp;
	int sockfd = -1;
	
	for (rp = result; rp != NULL; rp = rp->ai_next) {
		sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
		if (sockfd == -1)
			continue;		// Try next address
	
		if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1)
			break;	// Success
		
		close(sockfd);		// Connect failed: close this socket and try next address. 
	}
	if (rp == NULL)
		exit_with_error("Could not connect socket to any address.");
	
	freeaddrinfo(result);
	
	return sockfd;
}
//------------------------------------------------------------------------------------------------------------------
int main(int argc, char * argv[])
{		
	if (argc < 4)
		exit_with_error("missing parameters");

	tty_t tty(argv[2], argv[3]);
	
	if (tty._ready) {
		int server_fd = connect_to_seweb_server(argv[1]);
		
		if (server_fd > 0) {
			switch (fork()) {			
			case -1:
				exit_with_error("fork()");
			case 0:											// child
				process_server_requests(server_fd, tty);
				break;
			default:										// parent
				dispatch_serial_output_to_server(server_fd, tty);	
				break;
			}		
			close(server_fd);		
		}		
	}	
	return EXIT_SUCCESS;
}
